package com.csx.ops.service.node;

import com.csx.ops.dao.ConfigParse;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.annotations.XStreamAsAttribute;
import com.yh.csx.bsf.core.base.Callable;
import com.yh.csx.bsf.core.db.DbHelper;
import com.yh.csx.bsf.core.util.ConvertUtils;
import com.yh.csx.bsf.core.util.StringUtils;
import lombok.*;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.List;

@XStreamAlias("ShellNode")
public class ShellNode extends ConfigNode {
    @Getter
    @Setter
    @XStreamAlias("Server")
    private String server;
    @Getter @Setter
    @XStreamAlias("Shell")
    private String shell;



    @Override
    public void exec(){
        printParam(server,shell);
        var serverInfo = new ConfigParse().getServerList(null).get(server);
        if(serverInfo == null)
            throw new StateException("数据库信息未配置:"+ StringUtils.nullToEmpty(server));

        var ssh = new SshProvider(serverInfo.get("ip"),serverInfo.get("user"),serverInfo.get("password"),
                StringUtils.isEmpty(serverInfo.get("port"))?22: ConvertUtils.convert(serverInfo.get("port"),int.class));

        try {
            this.writeLog("shell执行开始");
            ssh.exec(shell, (line) -> {
                this.writeLog(line);
            });
            this.writeLog("shell执行完毕");
        }catch (Exception e){
            throw new StateException("shell执行出错",e);
        }

    }

    @Data
    @AllArgsConstructor
    public static class SshProvider{
        String ip;String user;String pwd;int port;
        public int exec(String shell, Callable.Action1<String> log){
            Session session = null;
            Channel channel = null;
            int returnCode = 0;
            //连接
            try {
                val jsch = new JSch();
                session = jsch.getSession(user, ip, port);
                session.setPassword(pwd);
                session.setConfig("StrictHostKeyChecking", "no");

                session.connect(10000);   // making a connection with timeout.
                // Create and connect channel.
                channel = session.openChannel("exec");
                ((ChannelExec) channel).setCommand(shell);
                channel.setInputStream(null);
                try(BufferedReader input = new BufferedReader(new InputStreamReader(channel.getInputStream()))){
                    channel.connect(10000);
                    // Get the output of remote command.
                    String line;
                    while ((line = input.readLine()) != null) {
                        log.invoke(line);
                    }
                }
                // Get the return code only after the channel is closed.
                if (channel.isClosed()) {
                    returnCode = channel.getExitStatus();
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
            finally {
                close(session, channel);
            }
            return returnCode;
        }

        private void close(Session session, Channel channel){
            try {
                if (channel.getOutputStream() != null)
                    //channel.getOutputStream().flush();
                    channel.getOutputStream().close();
            } catch (Exception e) {
            }
            try {
                if (channel.getInputStream() != null)
                    channel.getInputStream().close();
            } catch (Exception e) {
            }
            if (channel != null)
                try {
                    channel.disconnect();
                } catch (Exception exp) {
                }
            if (session != null)
                try {
                    session.disconnect();
                } catch (Exception exp) {
                }
        }
    }

    //从子节点设置对应的状态机
    public void setEngine(StateEngine engine){
        super.setEngine(engine);
    }
}
